import axios from 'axios'
import { ElInput } from "element-plus";
import { ref } from 'vue';

/* 
10. ReturnType用来得到函数的返回值的类型
InstanceType 用来得到类和组件的实例的类型
*/
function a10(a: number) {
  return 'abc'
}

let b10: ReturnType<typeof a10> = 'aa'
// b10 = 123  // error
let c10: ReturnType<typeof Date.now> = 123
// c10 = 'abc' // error


// 得到类的实例类型
class Student {
  name: string
  constructor(n: string) {
    this.name = n
  }
}

let d10: Student = new Student('age')
d10.name
// d10 = 'abc'
let e10: InstanceType<typeof Student> = new Student('aaa')
// e10 = 123
e10.name

// 得到组件实例的类型
const inputRef = ref<InstanceType<typeof ElInput>>()
inputRef.value?.focus()

/* 
9. typeof 
JS 的 typeof 用来得到类型名称
TS 的 typeof 用来得到类型
在TS中的 typeof 到底用的是 JS 的 还是 TS 的呢?
  看是否作为类型约束使用: TS的语法 
*/
let a9 = 3
console.log(typeof a9)  // 'number'
// 要求b9与a9的类型一致
let b9: typeof a9 = 5
// b9 = 'abc'  // error

// 得到函数的类型
function c9(a: number) {
  return a + 'abc'
}
const d9: typeof c9 = (b: number) => {
  return ''
}

// 得到对象的类型
const e9 = {
  a: 1,
  b: 'abc'
}
const e99: typeof e9 = {
  a: 2,
  b: 'cba'
}

// 得到类的类型
class Person {
  name: string
  constructor(n: string) {
    this.name = n
  }
}
const f9: typeof Person = class {
  name: string
  constructor () {
    this.name = ''
  }
  say(){}
}


/* 1. any类型 */
/* 
好处: 可以为任意类型值
坏处: 不再有编码提示
*/
let a: any = 1
a = 'abc'
a = {test: 'abc'}
a.test  // 没有正确语法的补全提示
a.test() // 没有错误提示

/* 2. 联合类型 | */
/* 
多个可选的类型或值的组合
*/
let a2: string|number = 'abc'
a2 = 3
// a2 = true // error

let a3: 1|2|3 = 2
// a3 = 4 // error

/* 
3. 类型断言
*/
/* 
我们告诉TS编译器变量的类型  =>  通过类型检查
*/
function getLength(val: string|number) {
  // return val.length  // error number没有length
  if ((val as string).length) {
    return (<string>val).length
  } else {
    return val.toString().length
  }
}

/* 
4. 枚举类型
*/
/* 
用来给多个数值或字符串取友好名称的类型
*/

enum Color {
  Red = 1,
  Green,
  Blue,
}
function handleColor(color: number) {
  if (color===Color.Red) {
    console.log('我要处理红色')
  } else if (color===Color.Green) {
    console.log('我要处理绿色')
  } else if (color===Color.Blue) {
    console.log('我要处理蓝色')
  } else {
    console.log('我要处理其它颜色')
  }
}

enum ApiPath {
  Login='/admin/acl/index/login',
  Logout='/admin/acl/index/logout',
  GetUserinfo='/admin/acl/index/info',
}

axios.post(ApiPath.Login)


/* 5. 接口 */
/* 
用来约束对象/函数/类, 项目中最多是约束对象
在项目开发中, 经常用接口来约束请求的后台数据结构
*/
interface UserInfoModel {
  name: string;
  avatar: string
  buttons: string[]
  roles: Array<string>
  routes: string[]
}

const userInfo: UserInfoModel = { // 后台返回的数据
  name: 'admin',
  avatar: '../xxx.jpg',
  buttons: ['a'],
  roles: ['aa'],
  routes: ['aaa'],
}

/* 
6. 泛型: 不确定的类型
  当有不确定的类型时, 尽量定义泛型
*/

// 创建一个数组, 数组中的元素是count个val
function createArray(val: any, count: number): any[] {
  const result: any[] = []
  for (let index = 0; index < count; index++) {
    result.push(val)
  }
  return result
}

const arr1 = createArray('abc', 3)  // ['abc', 'abc', 'abc']   any[]
arr1[0] // any
arr1[0].length2 // 没提示

const arr2 = createArray(5, 3)  // [5, 5, 5]  any[]

/* 
泛型可用于函数/类/接口, 开发中最多的是函数

泛型的3个操作
		定义泛型: 在定义的函数名右侧 <泛型名称>    T / K / V
		使用泛型: 在形参/返回值/函数体中   泛型名称
		指定具体类型: 调用函数时, 在函数名的右侧  <具体类型名称>
*/

function createArray2<T>(val: T, count: number): T[] {
  const result: T[] = []
  for (let index = 0; index < count; index++) {
    result.push(val)
  }
  return result
}

const arr3 = createArray2<string>('abc', 3) // string[]
arr3[0].length  // 有补全提示
// arr3[0].length2 // 有错误提示


// ref<number>(3)


/* 7. 理解axios请求方法的泛型 */
/* 
post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  3个泛型
    泛型1 T: 响应体数据的类型   {code: 200, data: 前端真正要操作数据} 是response.data的类型
    泛型2 R: 指定请求函数返回的promise的成功value的类型   是响应拦截器成功回调返回的数据的类型
    泛型3 D: 请求体参数类型   一般不用
  3个参数
    参数1: url
    参数2: 请求体参数data
    参数3: 配置对象: params/headers
  返回值: Promise
*/
// axios.post<T, R, D>(ApiPath.Login, {username:'admin', password: '111111'}, {headers: {token: 'xxx'}})
axios.post<T, R, D>(ApiPath.GetUserinfo)
    .then(
      response => {  // R就是response
        response.data // 得到的响应体
      }
    )
// 封装1
axios.interceptors.response.use(
  response => {
    return response.data
  }
)
axios.post<T, R, D>(ApiPath.GetUserinfo)
    .then(
      result => { // T一直都是response.data, 当前R也是 response.data

      }
    )
// 封装2
axios.interceptors.response.use(
  response => {
    return response.data.data
  }
)
axios.post<T, R, D>(ApiPath.GetUserinfo)
    .then(
      result => { // T一直都是response.data, 当前R也是 response.data.data, 也就是我们前端关注的数据类型

      }
    )
axios.post<any, UserInfoModel>(ApiPath.GetUserinfo)
// export {}